iT邦幫忙

2

Javascript基礎面試題目與筆記

  • 分享至 

  • xImage
  •  

作用域 (scope)

作用域指的是變數或函式在程式碼中的有效範圍,有全域和區域兩種,全域的作用域是文件中的全部程式碼,區域的作用域只在一個block裡面

  • 作用域鏈(scope chain)

    在原本的作用域裡沒有指定的變數,於是繼續往父層尋找,直到找到為止,這就是scope chain

  • 什麼是 hoisting?

    • hoisting的特性會出現在函式或變數上,讓他們的宣告提升到作用域頂端。在變數提升的特性中,如果在宣告var變數之前使用,會出現undefined,但如果在宣告前使用let和const會因為TDZ的關係報錯。在函式提升的特性中,函式表達式的提升行為會與它宣告變數的提升行為一樣。
  • var, let, const差別

    1. 作用域。var 是global scope或functional scope,let 和const是block scope。
    2. var跟let可以被重新賦值,const不行
    3. 三者都會被hoist,但只有var變數可以在被宣告前使用,const和let會因為TDZ的關係,如果在宣告變數之前就使用他們會報錯

JS特別要注意的地方(型別轉換與小數運算)

  • console.log((0.1+0.2) === 0.3)的結果是什麼?

    • 結果會是falsy,因為電腦是二進位制,電腦表示精準度有限,所以實際上小數點後面會有誤差,0.1加上0.2之後不會「剛好」等於0.3

    • 可以使用toFixed解決,設定精確到小數第一位
      (0.1+0.2).toFixed(1)

  • falsy value
    falsy value是指一個不完全是false的值,但當我們嘗試將它轉變成布林值時,我們會得到false。在JavaScript中,有5個falsy values,分別是0、空字串(empty string)、NaN、null及undefined。

  • 弱型別是甚麼

    js是弱型別的語言,在運算前會進行型別轉換,假設轉換後發現有字串,就會進行字串的相加,否則就是一般的數字相加。若想都以數字計算,假設有浮點數的話,可用Number(),或假設是字串和數字混雜的話,可用parseInt();另一方面,JS在減法時會將字串轉為數值,如果是 const str = "3" const num = 1 的話,會以數字做計算。Typescript是用來解決弱型別的問題

  • == 與===的差別在哪

    兩個等號是寬鬆相等,在比較時會做型別轉換,假設我們把字串1==數字1,字串會被轉換成數字,最後回傳true。
    三個等號不會進行型別轉換,所以如果上面兩個值去做嚴格相等比較,最後回傳false

    通常不建議用寬鬆相等,可能因為型別轉換造成預期外的結果。

閉包

  • 閉包的概念就是,當一個函數內部定義了另一個函式,即便外部函式執行完畢,內部函式依舊可以訪問外部函式中的變數
const a = 1;
console.log(a);

const outer = () => {
  let a = 1;
  const inner = () => {
    a += 1;
    console.log(a);
  };
  return inner;
};
const result = outer();
result(1);//output 2
result(1);//output 3

當我們呼叫 result() 時,實際上是在執行了 inner() 函式,而 inner 函式內部的 a 變數形成了一個閉包(Closure)。每次呼叫 result(),都能繼續存取並修改 a 的值,且 a 的值不會重新初始化。這是因為 a 變數被 inner 函式的作用域所捕獲,使得在每次呼叫 result() 時都能累加之前a的值。

AJAX

  • 同步與非同步、promise是什麼?

    • 同步就是按順序執行,一行程式碼執行完才會執行下一行的意思,但它有個問題,如果今天讀取檔案 (例如是背景圖片) 的程式碼在最前面,它採用同步執行而且花費超久的時間,使用者可能等很久頁面還是一片空白,因為處理事件的流程被「卡住」了

    • 非同步就是不一定要按照順序執行,如果 JS 沒有「非同步」的特性,網頁可能會跑兩行字,然後去拿圖片資源,當圖片沒有完全被載入,圖片後面的文字也無法出現,這對於使用者體驗很糟。有了JS「非同步」的特性,網頁中的文字會先完全顯示出來,等圖片被完全載入後再顯示出來,在圖片出現之前,使用者能閱讀網頁中的所有文字內容。

    • Promise是用來解決非同步事件處理,確保非同步事件完成後才繼續執行其他程式碼,在執行過程中可以看到三種狀態,分別是pending(進行中), fulfilled(已成功),跟rejected(已失敗),可以用.then()和.catch()語法去實作,更進階的語法是finally(),代表無論promise狀態無論fulfilled或rejected,都會執行finally()裡面的程式碼,如果網站裡有設置在向後端fetch資料時出現loading效果,使用finally()能確保無論資料有無回傳,都會關閉loading效果

  • 為何有callback hell、如何解決?

    • 原因是因為在處理非同步程式碼時寫太多層的callback functions,可以使用async/await 語法提升可讀性
  • async await 是什麼?

    • async/await 語法的操作原理和promise一樣,所以也被稱作Promise 的語法糖,比起Promise的寫法更像同步操作。用async 關鍵字把函式標記為異步函式,在處理promise時加await語法,用try block和catch block分別處理資料處理成功或失敗
    async function getData(){
    try{
        const res = await fetch("example-url",{method:"GET"});
     const data = await res.json();
     console.log(data);
    }catch(err){
     console.log(err)
    }
    }
    
  • Axios是什麼?使用它的優點為何?

    • 缺點是要安裝套件
    • 優點是語法更簡潔,在寫request method時可簡化為 axios.get("url") ,傳資料給server不需用JSON.stringify(),從server接收資料也不需要用json()就能使用
    • 完整程式碼如下
    //確定已經跑過 npm i axios
    
    const axios = require('axios');
    axios.get('url')
    .then(res=>{
        console.log('Data', res.data)
    }).catch(err=>{
        console.log(err)
    })
    

傳參考&傳值

如果是基本型別 (Primitive type),原始變數「不會」跟著複製變數的改變而變,表現出的行為是 pass by value。

如果是物件型別,且僅針對物件的內容做改變,原始變數「會」跟著複製變數的改變而有所不同,表現出的行為結果就是 pass by reference。傳參考表示變數指向的是同一個記憶體位置,修改其值就會導致共享該記憶體的其他變數一起被修改

const obj1 = { id: 1 };
const obj2 = obj1;

console.log(obj1 === obj2); //true  兩個變數共享同個記憶體位置

但要注意以下情境

let c = {name:'Sally'}
let d;

d=c;
c={name:'Angela'}

console.log(c); // {name:'Angela'}
console.log(d); // {name:'Sally'}

當我們將一個新的物件賦值給c時,實際上是創建了一個新的物件,並將它的參考賦值給了c。此時,c和d已經沒有任何關係了

深淺拷貝

在 JavaScript 中,多數的預設方法和運算子進行的通常是淺拷貝。然而,有一些方法並不是 JavaScript 核心語言的一部分,而是通過函式庫或框架提供的,可以執行深拷貝操作

  1. JSON.parse() 和 JSON.stringify()
const originalObj = { a: 1, b: { c: 2 } };
const deepCopiedObj = JSON.parse(JSON.stringify(originalObj));
originalObj.b.c = 100;

console.log(deepCopiedObj); // Output: { a: 1, b: { c: 2 } } 

深層複製需要耗費更多資源,不一定有必要。對於大多數情況來說,使用展開運算子進行淺層複製已經很夠了

展開運算子

展開運算子spread(...)

使用情境

  1. 複製現有陣列:如果你想要創建一個現有陣列的複製,你可以使用展開運算符將現有陣列的元素展開到一個新陣列中。例如,const newArray = [...oldArray] 將創建一個新陣列,運用的是淺拷貝。

  2. 合併陣列:如果你想要通過合併兩個或更多陣列的元素來創建一個新陣列,你可以使用展開運算符將每個陣列的元素展開到一個新陣列中。例如,const newArray = [...array1, ...array2] 將創建一個新陣列,其中包含 array1 的元素,然後是 array2 的元素。

  3. 將元素添加到陣列:如果你想要將一個或多個元素添加到現有陣列並創建一個新陣列,你可以使用展開運算符將現有陣列的元素展開到一個新陣列中,然後添加新元素。例如,const newArray = [...oldArray, newItem1, newItem2]

以上若有任何錯誤,都歡迎留言給我,謝謝


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言